﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using MediaSage.Lib;
using MediaSage.Lib.Configuration;
using MediaSage.Lib.Database;
using MediaSage.Lib.MediaUpdate;
using MediaSage.Lib.Plugin;
using MediaSage.Lib.Utilities;
using NHibernate.Criterion;

namespace MediaSageService.ScanFileSystemsTask
{
    public class ScanFileSystemTask : PluginBase
    {        

        public ScanFileSystemTask(Config configuration, PluginEntity pe)
            : base(configuration,pe)
        {            
        }

        /// <summary>
        /// Scans the filesystems for new files to process
        /// </summary>        
        public override bool  Run()
        {
            logger.Log("Entering MediaSageService.ScheduledTasks.ScanFileSystemTask()");
            var scannedExtensions = new List<string>();
            string logMsg = "Scanning extensions:";
            foreach (MediaFileTypesEntity mfte in config.MediaTypes)
            {
                scannedExtensions.Add(mfte.Extension.ToUpper());
                logMsg += " " + mfte.Extension;
            }

            logger.Log(logMsg);

            var newFiles = new List<string>();
            var updateFiles = new List<string>();
            foreach (FileSystemEntity mediaDir in config.FileSystems)
            {
                var retVal = getFilesToUpdate(mediaDir.directory, scannedExtensions);
                foreach(KeyValuePair<string,bool> kvp in retVal)
                {
                    if(kvp.Value)
                    {
                        updateFiles.Add(kvp.Key);
                    }
                    else
                    {
                        newFiles.Add(kvp.Key);
                    }
                }
            }

            MediaUpdate mediaUpdate=null;
            
            if(updateFiles.Count>0)
            {
                mediaUpdate = new MediaUpdate(config);
            }
            foreach(string file in updateFiles)
            {
                logger.Log(String.Format("Scheduling update for: {0}",file));
                
                mediaUpdate.UpdateMedia(file);
            }

            foreach(string file in newFiles)
            {
                logger.Log(String.Format("Guessing the name of: {0}",file));

            }

            logger.Log("Entering MediaSageService.ScheduledTasks.ScanFileSystemTask(), rc==true");
            return true;
        }

        
        /// <summary>
        /// Gets a list of files to update or add
        /// </summary>
        /// <param name="FileSystem"></param>
        /// <param name="ExtensionsToScan"></param>
        /// <returns></returns>
        private IDictionary<string,bool> getFilesToUpdate(string FileSystem, List<string> ExtensionsToScan)
        {
            logger.Log(
                String.Format(
                    "Entering MediaSageService.ScanFileSystemTask.getFilesToUpdate({0},{1},{2})",
                    FileSystem, string.Join(",", ExtensionsToScan.ToArray())));

            if (!Directory.Exists(FileSystem))
            {
                logger.Log(Logger.Levels.WARN, String.Format("{0} doesn't exist"));
                logger.Log(
                    String.Format(
                        "Exiting MediaSageService.ScanFileSystemTask.getFilesToUpdate({0},{1},{2}), rc=empty list",
                        FileSystem,  string.Join(",", ExtensionsToScan.ToArray())));
                return (new Dictionary<string,bool>());
            }

            var returnValue = new Dictionary<string,bool>();
            string[] files = Directory.GetFiles(FileSystem, "*.*", SearchOption.AllDirectories);
            foreach (string file in files)
            {
                var extension = Path.GetExtension(file);
                if (extension != null)
                    if (ExtensionsToScan.Contains(extension.ToUpper()))
                    {
                        // This is indeed a file we care about, check the last scan time
                        DateTime? lastMod = MediaFileScanTime(file);
                        if (lastMod == null)
                        {
                            logger.Log(String.Format("New file found: {0}", file));
                            returnValue.Add(file,false);
                        }
                        else
                        {
                            if (config.ForceRescan)
                            {
                                logger.Log(String.Format("Forced rescan file: {0}", file));
                                returnValue.Add(file, true);
                            }
                            else
                            {
                                // This has a timestamp, lets see if it is an update
                                TimeSpan delta = lastMod.Value.Subtract(DateTime.Now);
                                TimeSpan duration = new TimeSpan(config.RescanFrequency, 0, 0, 0, 0);

                                if (delta >= duration)
                                {
                                    logger.Log(String.Format("File: {0} is due for a rescan", file));
                                    returnValue.Add(file, true);
                                }
                            }
                        }
                    }
            }
            logger.Log(
                String.Format(
                    "Exiting MediaSageService.ScanFileSystemTask.getFilesToUpdate({0},{1},{2}), rc={3}",
                    FileSystem, string.Join(",", ExtensionsToScan.ToArray()),
                    string.Join(",", returnValue.ToArray())));
            return returnValue;
        }


        /// <summary>
        /// Checks to see if the mediafile is in the database, if it is returns the last mod time
        /// if not it returns null
        /// </summary>
        /// <param name="Filename">Filename of mediafile</param>
        /// <returns>Last scan time, or null if it doesnt exist</returns>
        private DateTime? MediaFileScanTime(string Filename)
        {
            logger.Log(String.Format("Entering MediaSageService.ScanFileSystemTask.MediaFileScanTime({0})", Filename));
            var entries = DBUtil.GetSession().CreateCriteria(typeof(VideoEntity)).Add(Restrictions.Eq("Filename", Filename)).List<VideoEntity>();
            if (entries == null || entries.Count == 0)
            {
                logger.Log(String.Format("Exiting MediaSageService.ScanFileSystemTask.MediaFileScanTime({0}), rc==null", Filename));
                return null;
            }

            logger.Log(String.Format("Exiting MediaSageService.ScanFileSystemTask.MediaFileScanTime({0}), rc=={1}", Filename, entries[0].LastModified));
            return (entries[0].LastModified);
        }

        public override string[] About()
        {
            VersionInfo verInfo = GetVersionInfo();
            ;
            string[] returnValue =
                {
                    "ScanFileSystemTask v" + verInfo.MajorVersion + "." + verInfo.MinorVersion + "." +
                    verInfo.BuildNumber
                };
            return returnValue;
        }

        public override IList<MediaFileTypesEntity> ExtensionsHandled()
        {
            throw new NotImplementedException();
        }

        public override PluginEntity GetConfiguration()
        {
            throw new NotImplementedException();
        }

        public override MediaClass GetMedia(MediaClass inputMedia, MediaMergeType mergeType)
        {
            throw new NotImplementedException();
        }

        public override MediaClass GetMediaByID(object ID)
        {
            throw new NotImplementedException();
        }

        public override MediaClass GetUpdateForMedia(MediaClass inputMedia, MediaMergeType MergeType)
        {
            throw new NotImplementedException();
        }

        public override VersionInfo GetVersionInfo()
        {
            VersionInfo verInfo;
            verInfo.Name = Assembly.GetExecutingAssembly().GetName().Name;
            verInfo.MajorVersion = Assembly.GetExecutingAssembly().GetName().Version.Major;
            verInfo.MinorVersion = Assembly.GetExecutingAssembly().GetName().Version.Minor;
            verInfo.BuildNumber = Assembly.GetExecutingAssembly().GetName().Version.Revision;
            return verInfo;
        }

        public override bool Run(MediaClass mediaObject)
        {
            throw new NotImplementedException();
        }

        public override List<MediaSearchReturn> SearchForMedia(MediaClass mediaObject)
        {
            throw new NotImplementedException();
        }

        public override List<MediaSearchReturn> SearchForMedia(MediaClass mediaObject, int maxReturns)
        {
            throw new NotImplementedException();
        }

        public override List<MediaSearchReturn> SearchForMediaByName(string name)
        {
            throw new NotImplementedException();
        }

        public override List<MediaSearchReturn> SearchForMediaByName(string name, int maxReturns)
        {
            throw new NotImplementedException();
        }

        public override List<MediaSearchReturn> SearchForMediaByNameAndYear(string name, int year)
        {
            throw new NotImplementedException();
        }

        public override List<MediaSearchReturn> SearchForMediaByNameAndYear(string name, int year, int maxReturns)
        {
            throw new NotImplementedException();
        }

        public override IList<PersonEntity> SearchPersonByName(string name)
        {
            throw new NotImplementedException();
        }

        public override void SetDefaultConfig()
        {            
        }
    }
}
